﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Tomers.WPF.Shapes;
using System.Threading;
using System.IO;
using System.Data;
using System.Windows.Forms;

namespace epk23.PartIIProject
{
    /// <summary>
    /// Interaction logic for MachineWindow.xaml
    /// </summary>
    public partial class MachineWindow : Window
    {
        private MachineGraph machineGraph;

        private Interpreter interpreter;
        private SpeedManager speedManager;

        private int rulesExecuted;

        private DataSet _ds;
        private DataTable table;

        private FolderBrowserDialog folderBrowserDialog;

        DataRow row0;

        bool datagridInFocus;

        public MachineWindow()
        {
            InitializeComponent();
            RuleNode.window = this;

            MachineGraph.window = this;
            MachineGraph.canvas = machineCanvas;
            machineGraph = new MachineGraph();

            RuleNode.graph = machineGraph;

            interpreter = new Interpreter();
            interpreter.machineWindow = this;

            speedManager = new SpeedManager();

            interpreter.speedManager = speedManager;

            refreshComboMachines();

            folderBrowserDialog = new FolderBrowserDialog();
            folderBrowserDialog.ShowNewFolderButton = true;
            folderBrowserDialog.RootFolder = System.Environment.SpecialFolder.MyComputer;

            table = new DataTable();
            table.RowChanged += new DataRowChangeEventHandler(tableRowChanged);
            _ds.Tables.Clear();
            _ds.Tables.Add(table);
            grdRegValues.DataContext = _ds.Tables[0];

            DataColumn colStep = new DataColumn("Step", typeof(int));
            table.Columns.Add(colStep);
        }

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            _ds = new DataSet();
        }

        private void refreshComboMachines()
        {
            foreach (string s in Directory.GetFiles(FileManager.folderName))
            {
                if (s.Length > 4)
                {
                    if (s.EndsWith(".regm"))
                    {
                        String machineName = s.Remove(0, FileManager.folderName.Length);
                        machineName = machineName.Remove(machineName.Length - 5);
                        comboMachines.Items.Add(machineName);
                    }
                }
            }
        }

        private void cmdAddIncrNode_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.addIncrRuleNode(20, 20, txtRegName.Text, machineCanvas);
            refreshRegValues();
        }

        private void cmdAddDecrNode_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.addDecrRuleNode(150, 20, txtRegName.Text, machineCanvas);
            refreshRegValues();
        }

        private void cmdAddHaltNode_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.addHaltRuleNode(280, 20, machineCanvas);

        }

        private void cmdAddMachineNode_Click(object sender, RoutedEventArgs e)
        {
            if ((string)comboMachines.SelectedItem != "" && comboMachines.SelectedItem != null)
            {
                machineGraph.addMachineNode(150, 150, (string)comboMachines.SelectedItem, machineCanvas);
                refreshRegValues();
                machineGraph.resetNodeMarker();
            }
        }

        private void cmdPrintRules_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.machine.printRules();
        }

        private void cmdRun_Click(object sender, RoutedEventArgs e)
        {
            if (machineGraph.Nodes.Count > 0 && interpreter.running == false)
                run();
        }

        private void run()
        {
            rulesExecuted = 0;

            grdRegValues.IsReadOnly = true;

            //foreach (Register r in machineGraph.machine.registers)
            //{

            //    DataColumn col = new DataColumn(r.regName, typeof(int));
            //    table.Columns.Add(col);
            //}

            speedManager.Speed = (int)sliderSpeed.Value;
            Machine m = machineGraph.machine;

            machineGraph.resetNodeMarker();

            interpreter.run(m);
        }

        private void cmdStep_Click(object sender, RoutedEventArgs e)
        {
            if (machineGraph.Nodes.Count > 1)
            {
                if (interpreter.running == false)
                {
                    run();
                    Thread.Sleep(50);
                }
                interpreter.semStep.Release();
            }
        }

        private void sliderSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (speedManager != null) //to prevent exceptions from event firing at initialization
            {
                speedManager.Speed = (int)sliderSpeed.Value;

                if (sliderSpeed.Value == 0)
                    cmdStep.IsEnabled = true;
                else
                {
                    cmdStep.IsEnabled = false;
                    //If the interpreter is waiting on the step semaphore, must release it to continue execution.
                    if (speedManager.Waiting) interpreter.semStep.Release();
                }
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            interpreter.thread.Abort();
        }

        private void cmdSave_Click(object sender, RoutedEventArgs e)
        {
            if (txtMachineName.Text.Contains(' '))
            {
                System.Windows.MessageBox.Show("Register Machine names cannot contain spaces. Please enter a valid name.", "Machine name error", MessageBoxButton.OK);
                return;
            }
            bool exists = false;
            foreach (String s in Directory.GetFiles(FileManager.folderName))
            {
                if (s.Remove(0, FileManager.folderName.Length) == txtMachineName.Text + ".regm")
                {
                    exists = true;
                    break;
                }
            }
            if (!exists)
                comboMachines.Items.Add(txtMachineName.Text + ".regm");

            FileManager.writeFile(machineGraph, txtMachineName.Text, txtboxDescription.Text);
        }

        private void txtMachineName_GotFocus(object sender, RoutedEventArgs e)
        {
            if (txtMachineName.Text == "Machine Name")
                txtMachineName.Text = "";
        }

        private void cmdLoad_Click(object sender, RoutedEventArgs e)
        {
            if (comboMachines.SelectedItem != null)
            {
                clearCanvas();
                string selectedMachineName = (string)comboMachines.SelectedItem;
                MachineWithLayout machineWithLayout = FileManager.readFile(selectedMachineName);

                MachineGraph.loadGraph(machineWithLayout, machineGraph);

                machineGraph.makeNodeMarker();
                machineGraph.resetNodeMarker();
                refreshRegValues();

                txtMachineName.Text = selectedMachineName;
                txtboxDescription.Text = machineWithLayout.Machine.description;
            }
        }

        private void clearCanvas()
        {
            machineCanvas.Children.Clear();
        }

        private void cmdClear_Click(object sender, RoutedEventArgs e)
        {
            clearCanvas();
            machineGraph.clearGraph();
            machineGraph.makeNodeMarker();
            refreshRegValues();
        }

        public void refreshRegValues()
        {
            table.Rows.Clear();
            table.Columns.Clear();

            DataColumn colStep = new DataColumn("Step", typeof(int));
            table.Columns.Add(colStep);

            row0 = table.NewRow();
            table.Rows.Add(row0);
            row0[0] = 0;

            foreach (Register r in machineGraph.machine.registers)
            {
                DataColumn col = new DataColumn(r.regName, typeof(int));
                table.Columns.Add(col);
                row0[r.regName] = r.value;
            }

            grdRegValues.DataContext = null;
            grdRegValues.DataContext = _ds.Tables[0];

        }

        private void comboMachines_DropDownOpened(object sender, EventArgs e)
        {
            comboMachines.Items.Clear();
            refreshComboMachines();
        }

        private void cmdSetStart_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.settingStart = true;
        }

        public void ruleExecuting(Rule rule)
        {
            machineGraph.setMarkedNode(machineGraph.lookupNode(rule.ruleNumber));
        }

        public void ruleExecuted()
        {
            rulesExecuted++;

            DataRow row = table.NewRow();
            table.Rows.Add(row);

            row["Step"] = rulesExecuted;

            foreach (Register r in machineGraph.machine.registers)
            {
                row[r.regName] = r.value;
            }

            if (grdRegValues.Items.Count > 0)
            {
                var border = VisualTreeHelper.GetChild(grdRegValues, 0) as Decorator;
                if (border != null)
                {
                    var scroll = border.Child as ScrollViewer;
                    if (scroll != null) scroll.ScrollToEnd();
                }
            }
        }

        public void executionFinished()
        {
            grdRegValues.IsReadOnly = false;
        }

        private void cmdUndo_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.undo();

            refreshRegValues();
        }

        private void cmdRedo_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.redo();
        }

        private void cmdChangeRegister_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.changeRegister(txtRegName.Text);
        }

        private void cmdTerminate_Click(object sender, RoutedEventArgs e)
        {
            if (interpreter.thread != null)
                interpreter.thread.Abort();

            interpreter.running = false;

            if (machineGraph.Nodes.Count != 0)
                machineGraph.resetNodeMarker();

            grdRegValues.IsReadOnly = false;
        }

        private void cmdChooseStorageLocation_Click(object sender, RoutedEventArgs e)
        {
            string path = selectFolder();

            if (path == "")
                path = "C:\\Machines";

            FileManager.folderName = path + "\\";

            lblSaveLocation.Content = path;
            comboMachines.Items.Clear();
        }

        private string selectFolder()
        {
            folderBrowserDialog.ShowDialog();
            string path = folderBrowserDialog.SelectedPath;
            return path;
        }

        public void showDescription(string description)
        {
            txtblockDescriptionMouseover.Text = description;
        }

        private void tableRowChanged(object sender, DataRowChangeEventArgs e)
        {
            if (datagridInFocus)
            {
                foreach (Register r in machineGraph.machine.registers)
                {
                    r.value = (int)e.Row[r.regName];
                }
            }
        }

        private void grdRegValues_GotFocus(object sender, RoutedEventArgs e)
        {
            datagridInFocus = true;
        }

        private void grdRegValues_LostFocus(object sender, RoutedEventArgs e)
        {
            datagridInFocus = false;
        }

        private void cmdClearRegValues_Click(object sender, RoutedEventArgs e)
        {
            refreshRegValues();
        }

        private void tabRunMachine_MouseDown(object sender, MouseButtonEventArgs e)
        {

        }

        private void tabBuildMachine_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            txtblockTooltip.Text = Values.BuildMachineTabTooltip;
        }

        private void tabRunMachine_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            txtblockTooltip.Text = Values.RunMachineTabTooltip;
        }

        private void tabSaveLoad_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            txtblockTooltip.Text = Values.SaveLoadTabTooltip;
        }

        private void tabBuildMachine_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            txtblockTooltip.Text = Values.WindowTooltip;
        }

        private void tabRunMachine_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            txtblockTooltip.Text = Values.WindowTooltip;
        }

        private void tabSaveLoad_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            txtblockTooltip.Text = Values.WindowTooltip;
        }

        private void cmdZeroRegisters_Click(object sender, RoutedEventArgs e)
        {
            machineGraph.zeroRegisters();
        }

        private void machineCanvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            machineGraph.canvasMouseMove(e);
        }

        private void machineCanvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            machineGraph.canvasMouseUp(e);
        }

        private void lblRightBoundary_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            machineGraph.endDrag();
        }

        private void txtblockDescriptionMouseover_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            machineGraph.endDrag();
        }
    }
}
